package com.huangfuyi.process.service;

import com.huangfuyi.process.actuator.enums.EPNodeStatus;
import com.huangfuyi.process.actuator.enums.EPParamType;
import com.huangfuyi.process.dao.EPCoreRunningDao;
import com.huangfuyi.process.entity.running.EPParamEntity;
import com.huangfuyi.process.entity.running.EPRunningEntity;
import com.huangfuyi.process.entity.running.EPRunningWithParamsEntity;
import com.huangfuyi.process.exceptions.EPDaoException;
import com.huangfuyi.process.exceptions.EPServiceException;
import com.huangfuyi.process.utils.EPIdUtils;
import com.huangfuyi.process.utils.EPObjectUtils;

import java.util.*;

public class EPCoreRunningService<T extends EPRunningEntity, E extends EPParamEntity> {

    private final EPCoreRunningDao<T, E> runningDao;

    /**
     * 更新节点运行信息
     * 1、运行状态及时间更新
     * 2、运行信息缺失时，替换为空字符串
     * 3、对各个参数进行合法性校验
     */
    public void updateAndAppendMessage(String runningId, EPNodeStatus nodeStatus, String runningMessage) {
        if (EPObjectUtils.isEmpty(runningId)) {
            throw new EPServiceException("运行时id不能为空。");
        }

        if (EPObjectUtils.isEmpty(nodeStatus)) {
            throw new EPServiceException("节点运行状态不能为空。");
        }

        if (EPObjectUtils.isEmpty(runningMessage)) {
            runningMessage = "";
        }

        int updated = runningDao.updateAndAppendMessage(runningId, nodeStatus, runningMessage, System.currentTimeMillis());
        if (updated != 1) {
            throw new EPDaoException(String.format(Locale.ROOT, "更新节点运行状态异常，持久层返回更新数据条数为：%d, 期望的返回值：1", updated));
        }

    }

    /**
     * 使用运行时id查询运行时信息
     */
    public T queryById(String runningId) {
        T runningEntity = runningDao.queryById(runningId);
        if (EPObjectUtils.isEmpty(runningEntity)) {
            throw new EPDaoException(String.format(Locale.ROOT, "未查询到运行时信息，请核对运行时id是否正确。runningId：%s", runningId));
        }
        return runningEntity;
    }

    /**
     * 使用运行时id查询运行时信息，包含运行时参数信息
     */
    public EPRunningWithParamsEntity queryWithParamsById(String runningId) {
        EPRunningEntity runningEntity = runningDao.queryById(runningId);

        if (EPObjectUtils.isEmpty(runningEntity)) {
            throw new EPServiceException(String.format(Locale.ROOT, "未查询到运行时信息，请核对运行时id是否正确。runningId：%s", runningId));
        }

        HashMap<String, Object> paramsIn = new HashMap<>();
        HashMap<String, Object> paramsOut = new HashMap<>();
        List<E> params = runningDao.queryParamsByRunningId(runningId);
        if (!EPObjectUtils.isEmpty(params)) {
            for (E param : params) {
                EPParamType paramType = param.getParamType();
                if (EPParamType.IN.equals(paramType)) {
                    paramsIn.put(param.getKey(), param.getValue());
                } else if (EPParamType.OUT.equals(paramType)) {
                    paramsOut.put(param.getKey(), param.getValue());
                }
            }
        }

        EPRunningWithParamsEntity runningInfo = new EPRunningWithParamsEntity();
        runningInfo.setId(runningEntity.getId());
        runningInfo.setInstanceId(runningEntity.getInstanceId());
        runningInfo.setNodeId(runningEntity.getNodeId());
        runningInfo.setCreateTime(runningEntity.getCreateTime());
        runningInfo.setUpdateTime(runningEntity.getUpdateTime());
        runningInfo.setStatus(runningEntity.getStatus());
        runningInfo.setMessage(runningEntity.getMessage());
        runningInfo.setParamsIn(paramsIn);
        runningInfo.setParamsOut(paramsOut);

        return runningInfo;
    }

    /**
     * 查询指定流程实例的所有节点运行信息
     */
    public List<T> queryByInstanceId(String instanceId) {
        List<T> runningEntities = runningDao.queryByInstanceId(instanceId);
        if (EPObjectUtils.isEmpty(runningEntities)) {
            throw new EPDaoException(String.format(Locale.ROOT, "未查询到指定流程实例的节点运行信息，请核对流程实例id是否正确。instance id: %s", instanceId));
        }
        return runningEntities;
    }

    /**
     * 查询流程实例已运行节点的数量
     */
    public int countByInstanceId(String instanceId) {
        return runningDao.countByInstanceId(instanceId);
    }

    /**
     * 保存运行信息
     */
    public <K extends EPRunningEntity> void add(K runningInfo) {
        if (EPObjectUtils.isEmpty(runningInfo)) {
            throw new EPServiceException("运行信息不能为空。");
        }
        if (EPObjectUtils.isEmpty(runningInfo.getInstanceId())) {
            throw new EPServiceException("所属流程实例id不能为空。");
        }
        if (EPObjectUtils.isEmpty(runningInfo.getNodeId())) {
            throw new EPServiceException("当前运行节点id不能为空。");
        }
        if (EPObjectUtils.isEmpty(runningInfo)) {
            runningInfo.setId(EPIdUtils.getId());
        }

        runningInfo.setCreateTime(System.currentTimeMillis());
        runningInfo.setUpdateTime(System.currentTimeMillis());
        runningInfo.setStatus(EPNodeStatus.READY);

        int add = runningDao.add(runningInfo);
        if (add != 1) {
            throw new EPDaoException(String.format(Locale.ROOT, "保存节点运行信息异常，持久层返回保存的数据条数为：%d, 期望的返回值：1", add));
        }
    }

    /**
     * 保存节点运行参数
     */
    public void saveParams(String instanceId, String runningId, EPParamType paramType, Map<String, Object> params) {
        if (EPObjectUtils.isEmpty(instanceId)) {
            throw new EPServiceException("参数所属流程实例id不能为空。");
        }
        if (EPObjectUtils.isEmpty(runningId)) {
            throw new EPServiceException("参数所属运行时id不能为空。");
        }
        if (EPObjectUtils.isEmpty(paramType)) {
            throw new EPServiceException("参数类型不能为空。期望值： EPParamType.IN 或 EPParamType.OUT");
        }
        if (EPObjectUtils.isEmpty(instanceId)) {
            return;
        }

        ArrayList<EPParamEntity> paramEntities = new ArrayList<>();
        for (Map.Entry<String, Object> me : params.entrySet()) {
            EPParamEntity paramEntity = new EPParamEntity();
            paramEntity.setInstanceId(instanceId);
            paramEntity.setRunningId(runningId);
            paramEntity.setParamType(paramType);
            paramEntity.setKey(me.getKey());
            paramEntity.setValue(me.getValue());
            paramEntity.setValueType(EPObjectUtils.isEmpty(me.getValue()) ? "nullValue" : me.getValue().getClass().getTypeName());
            paramEntities.add(paramEntity);
        }

        int saved = runningDao.saveParams(paramEntities);
        if (saved != params.size()) {
            throw new EPDaoException(String.format(Locale.ROOT, "保存节点运行参数异常，持久层返回保存的参数数量为：%d, 期望的返回值：%d", saved, params.size()));
        }
    }

    public EPCoreRunningService(EPCoreRunningDao<T, E> runningDao) {
        this.runningDao = runningDao;
    }
}
